﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Data.SqlClient;
using OCDStudio.SqlAzureBackup.Client.Properties;
using System.IO;

namespace OCDStudio.SqlAzureBackup.Client.Code
{
    [Serializable]
    public class BackupProfile
    {
        public string ProfileName { get; set; }
        public string Server { get; set; }
        public string Catalog { get; set; }
        public string User { get; set; }
        public string Password { get; set; }
    }

    public enum RestoreIssue{
        None,
        DuplicateKey,
        GenericIssue
    }

    public class BackupUtils
    {

        public static Action<string> Logger { get; set; }

        static BackupUtils()
        {
            Logger = (p) => { };
        }

        public static bool CheckConnection(BackupProfile prof)
        {
            return CheckConnection(prof.Server, prof.Catalog, prof.User, prof.Password);
        }

        public static string[] GetTables(BackupProfile prof)
        {
            List<string> tables = new List<string>();
            Logger("Obtaining table list...");
            using (var conn = new SqlConnection(string.Format(Settings.Default.ConnectionString,
                prof.Server, prof.Catalog, prof.User, prof.Password)))
            {
                Logger("Opening connection...");
                conn.Open();
                var comm = conn.CreateCommand();
                comm.CommandType = System.Data.CommandType.Text;
                comm.CommandText = "select * from sysobjects where xtype='U'";
                var res = comm.ExecuteReader();
                while (res.Read())
                {
                    Logger("Found: "+res.GetString(0));
                    tables.Add(res.GetString(0));
                }
            }
            return tables.ToArray();
        }

        public static bool CheckConnection(string server, string catalog, string user, string password)
        {
            SqlConnection conn = new SqlConnection(
                    string.Format(Settings.Default.ConnectionString, server,catalog,user,password));
            try
            {
                Logger("Connecting...");
                conn.Open();
                conn.Close();
                Logger("Connection successful");
                return true;
            }
            catch (Exception)
            {
                Logger("Connection refused!");
                return false;
            }
        }

        public static bool CheckPrerequisites(params string[] commands)
        {
            Logger("Checking pre-requisites...");
            foreach (var item in commands)
            {
                var p = new Process();
                p.StartInfo.FileName = item;
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.CreateNoWindow = true;
                try
                {
                    p.Start();
                }
                catch (Exception)
                {
                    Logger("Failed!");
                    return false;
                }
                p.WaitForExit();
            }
            Logger("Success!");
            return true;
        }

        public static bool BackupDatabase(string path, BackupProfile prof, IEnumerable<string> tbs, int retries=3)
        {
            var now = DateTime.Now;
            var todayPath = now.Year.ToString() + now.Month.ToString().PadLeft(2, '0') + now.Day.ToString().PadLeft(2, '0');
            Logger("Backup in folder: " + todayPath);
            Directory.CreateDirectory(Path.Combine(path, todayPath));

            string args = "{0}.dbo.{1} out \"{2}\" -U {3}@{4} -S tcp:{4}.database.windows.net -P {5} -n -q";
            Logger("Connecting...");
            var local = Stopwatch.StartNew();
            var global = Stopwatch.StartNew();
            foreach (var item in tbs)
            {
                Logger("Processing "+item);
                string localProcess = string.Format(args, prof.Catalog,item,
                    Path.Combine(path, todayPath, item + ".dat"),prof.User,prof.Server.Split('.')[0],prof.Password);
                for (int i = 0; i < retries; i++)
                {
                    var proc = new Process();
                    proc.StartInfo.FileName = "bcp";
                    proc.StartInfo.Arguments = localProcess;
                    proc.StartInfo.RedirectStandardOutput = true;
                    proc.StartInfo.RedirectStandardError = true;
                    //proc.EnableRaisingEvents = true;
                    proc.StartInfo.CreateNoWindow = true;
                    proc.StartInfo.UseShellExecute = false;
                    
                    // see below for output handler
                    //proc.ErrorDataReceived += new DataReceivedEventHandler(proc_ErrorDataReceived);
                    //proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);


                    proc.Start();

                    Logger(proc.StandardOutput.ReadToEnd());
                    Logger(proc.StandardError.ReadToEnd());
                    //proc.BeginOutputReadLine();

                    proc.WaitForExit();
                    if (proc.ExitCode == 0) break;
                    else if (i == (retries - 1)) return false;
                }
                Logger("Elapsed: " + local.Elapsed.TotalSeconds.ToString());
                local.Restart();
            }
            global.Stop();
            Logger("Completed in: " + global.Elapsed.ToString());
            return true;
        }

        static void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            Logger("BCP: " + e.Data);
        }

        static void proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            Logger("Error: " + e.Data);
        }
        //TODO devi gestire le chiavi!!!!
        public static RestoreIssue RestoreDatabase(string path, BackupProfile prof, IEnumerable<string> tbs,int retries=3)
        {   
            Logger("Restoring from folder: " + path);
            
            string args = "{0}.dbo.{1} in \"{2}\" -U {3}@{4} -S tcp:{4}.database.windows.net -P {5} -n -q -E";
            Logger("Connecting...");
            var local = Stopwatch.StartNew();
            var global = Stopwatch.StartNew();
            foreach (var item in tbs)
            {
                Logger("Processing " + item);
                string localProcess = string.Format(args, prof.Catalog, item,
                    Path.Combine(path, item + ".dat"), prof.User, prof.Server.Split('.')[0], prof.Password);
                for (int i = 0; i < retries; i++)
                {
                    var proc = new Process();
                    proc.StartInfo.FileName = "bcp";
                    proc.StartInfo.Arguments = localProcess;
                    proc.StartInfo.RedirectStandardOutput = true;
                    proc.StartInfo.RedirectStandardError = true;
                    //proc.EnableRaisingEvents = true;
                    proc.StartInfo.CreateNoWindow = true;
                    proc.StartInfo.UseShellExecute = false;

                    // see below for output handler
                    //proc.ErrorDataReceived += new DataReceivedEventHandler(proc_ErrorDataReceived);
                    //proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);


                    proc.Start();

                    string output = proc.StandardOutput.ReadToEnd();
                    Logger(output);
                    string error = proc.StandardError.ReadToEnd();
                    Logger(error);
                    

                    proc.WaitForExit();
                    if (proc.ExitCode == 0) break;
                    else if (i == (retries - 1))
                    {
                        if (output.Contains("Cannot insert duplicate key"))
                            return RestoreIssue.DuplicateKey;
                        else
                            return RestoreIssue.GenericIssue;
                    }
                        
                }
                Logger("Elapsed: " + local.Elapsed.TotalSeconds.ToString());
                local.Restart();
            }
            global.Stop();
            Logger("Completed in: " + global.Elapsed.ToString());
            return RestoreIssue.None;
        }
    }
}
